package fun.easycode.jointblock.core;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import fun.easycode.jointblock.exception.CheckException;
import fun.easycode.jointblock.util.LogUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 动态查询工具类，用于MyBatis Plus
 * @author xuzhe
 */
@Slf4j
public final class DynamicOperate {

    /**
     * 等于
     */
    public static final String EQ = "Eq";
    /**
     * 模糊查询
     */
    public static final String LIKE = "Like";
    /**
     * 模糊查询左
     */
    public static final String LIKE_LEFT = "LikeLeft";
    /**
     * 模糊查询右
     */
    public static final String LIKE_RIGHT = "LikeRight";
    /**
     * 大于
     */
    public static final String GT = "Gt";
    /**
     * 小于
     */
    public static final String LT = "Lt";
    /**
     * 大于等于
     */
    public static final String GE = "Ge";
    /**
     * 小于等于
     */
    public static final String LE = "Le";

    /**
     * in 集合查询
     */
    public static final String IN = "In";

    /**
     * not in 集合查询
     */
    public static final String NOT_IN = "NotIn";

    /**
     * is null 查询
     */
    public static final String IS_NULL = "IsNull";

    /**
     * 排序
     */
    public static final String SORT = "Sort";
    /**
     * 跳过字段， queryWrapper中跳过
     */
    public static final List<String> SKIP_FIELDS = Arrays.asList("size", "current");

    /**
     * @param qry           查询指令
     * @param mapper        查询mapper
     * @param resultConvert 查询实体返回转换逻辑
     * @param <T>           数据库实体
     * @param <K>           查询指令
     * @param <R>           转换的返回结果
     * @return 要返回的实体
     */
    public static <T, K, R> List<R> list(K qry
            , BaseMapper<T> mapper
            , QueryResultConvert<T, R> resultConvert, String... skipFields) {
        QueryWrapper<T> queryWrapper = queryWrapper(qry, skipFields);
        List<T> result = mapper.selectList(queryWrapper);
        return result.stream().map(resultConvert::to)
                .collect(Collectors.toList());
    }

    /**
     * 分页查询工具方法
     * @param qry 查询指令
     * @param mapper 查询mapper
     * @param resultConvert 查询实体返回转换逻辑
     * @return 要返回的实体
     * @param <T> 数据库实体
     * @param <K> 查询指令
     * @param <R> 转换的返回结果
     */
    public static <T,K extends PagingQry, R> PageDto<R> page(K qry
            , BaseMapper<T> mapper
            , QueryResultConvert<T,R> resultConvert){
        QueryWrapper<T> queryWrapper = queryWrapper(qry);
        Page<T> page = mapper.selectPage(new Page<>(qry.getCurrent(), qry.getSize()), queryWrapper);
        return PageDto.toPageDto(page, resultConvert::to);
    }

    /**
     *  修改工具方法，注意此方法cmd中的值必须传入，如果未传，数据库会被设置为Null
     * @param cmd 修改指令实体
     * @param mapper mybatis mapper
     * @param callback 修改回调，用于自定义修改逻辑
     * @param resultConvert 修改实体返回转换
     * @param skipFields 跳过字段，用于那些需要自定义逻辑的字段配合修改回调
     * @return 返回修改后的实体
     * @param <T> 数据库实体
     * @param <K> 修改指令
     * @param <R> 修改后的实体
     */
    public static <T, K, R> Optional<R> update(K cmd, BaseMapper<T> mapper
            , UpdateCallback<T, K> callback
            , UpdateResultConvert<T, R> resultConvert
            , String... skipFields) {
        return update(cmd, mapper, callback, resultConvert, false, false, skipFields);
    }

    /**
     * 修改工具方法，注意此方法cmd中的值必须传入，如果未传，数据库会被设置为Null
     * 此方法进行审计信息的填充update_by、update_time
     * 单个对象修改，根据条件必须唯一锁定一条数据
     *
     * @param cmd           修改指令实体
     * @param mapper        mybatis mapper
     * @param callback      修改回调，用于自定义修改逻辑
     * @param resultConvert 修改实体返回转换
     * @param skipFields    跳过字段，用于那些需要自定义逻辑的字段配合修改回调
     * @param <T>           数据库实体
     * @param <K>           修改指令
     * @param <R>           修改后的实体
     * @return 返回修改后的实体
     */
    public static <T, K, R> Optional<R> updateNotAudit(K cmd, BaseMapper<T> mapper
            , UpdateCallback<T, K> callback
            , UpdateResultConvert<T, R> resultConvert
            , String... skipFields) {
        return update(cmd, mapper, callback, resultConvert, false, true, skipFields);
    }

    /**
     * 修改工具方法
     *
     * @param cmd           修改指令实体
     * @param mapper        mybatis mapper
     * @param callback      修改回调，用于自定义修改逻辑
     * @param resultConvert 修改实体返回转换
     * @param skipEmpty     是否跳过cmd的empty字段，如果要跳过，那么不传入的字段，或者是""这种都不进行修改
     * @param skipAudit     是否跳过审计字段update_by和update_time就不自动传参
     * @param skipFields    跳过字段，用于那些需要自定义逻辑的字段配合修改回调
     * @param <T>           数据库实体
     * @param <K>           修改指令
     * @param <R>           修改后的实体
     * @return 返回修改后的实体
     */
    private static <T, K, R> Optional<R> update(K cmd
            , BaseMapper<T> mapper
            , UpdateCallback<T, K> callback
            , UpdateResultConvert<T, R> resultConvert
            , boolean skipEmpty
            , boolean skipAudit
            , String... skipFields) {

        Map<String, Field> fieldMap = ReflectUtil.getFieldMap(cmd.getClass());
        List<String> skipFieldList = Arrays.asList(skipFields);
        // 生成修改id, 用来记录修改的具体情况，用于打印日志
        String updateId = IdUtil.getSnowflakeNextIdStr();
        UpdateWrapper<T> wrapper = new LogUpdateWrapper<>(updateId);
        Queue<Object> updateQueue = new LinkedList<>();

        for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
            // 如果字段要跳过则方法对这个字段不进行任何处理
            if (skipFieldList.stream().anyMatch(skipField -> ObjectUtil.equal(skipField, entry.getKey()))) {
                continue;
            }
            // 在修改的过程中如果被标记条件则称为修改条件
            if (AnnotationUtil.hasAnnotation(entry.getValue(), Condition.class)) {
                wrapper(cmd, entry.getKey(), entry.getValue(), wrapper);
            } else {
                // 如果没有被标记为条件则进行修改
                String fieldName = getDatabaseFieldName(-1, entry.getKey(), entry.getValue());
                Object fieldValue = ReflectUtil.getFieldValue(cmd, entry.getValue());
                if (ObjectUtil.isNotEmpty(fieldValue)) {
                    updateQueue.offer(fieldName);
                    updateQueue.offer(fieldValue);
                } else if (!skipEmpty) {
                    // 如果设置跳过empty则为empty的时候不进行修改
                    updateQueue.offer(fieldName);
                    updateQueue.offer(null);
                }
            }
        }
        T entity = mapper.selectOne(wrapper);
        if (entity != null) {
            if (callback != null) {
                callback.callback(cmd, wrapper);
            }
            while (!updateQueue.isEmpty()) {
                String updateFieldName = (String) updateQueue.remove();
                Object updateFieldValue = updateQueue.remove();
                wrapper.set(updateFieldName, updateFieldValue);
            }

            // 当skipAudit == true, 跳过审计填充
            if (!skipAudit) {
                // 审计信息支持
                wrapper.set(camel2under("lastModifiedDate"), LocalDateTime.now());
                wrapper.set(camel2under("lastModifiedBy"), UserHolder.getInstance().getUserId());
            }
            mapper.update(null, wrapper);
            log.info("update {}: op {} info {}."
                    , entity.getClass().getSimpleName(), UserHolder.getInstance().getUserId()
                    , LogUtil.getFieldUpdateLog(updateId, entity));
            if (resultConvert == null) {
                return Optional.empty();
            } else {
                return Optional.of(resultConvert.to(entity));
            }
        }
        throw new CheckException("要修改的实体不存在!");
    }

    /**
     * 批量修改，自动填充审计lastModifiedDate lastModifiedBy
     * 如果值为null会被设置成null
     *
     * @param cmd
     * @param mapper
     * @param skipFields
     * @param <T>
     * @param <K>
     */
    public static <T, K> void updateBatch(K cmd
            , BaseMapper<T> mapper
            , UpdateCallback<T, K> callback
            , String... skipFields) {
        UpdateWrapper<T> updateWrapper = updateWrapper(cmd, false, false, false, skipFields);
        updateWrapper = callback.callback(cmd, updateWrapper);
        String whereSqlSegment = getWhereSqlSegment(updateWrapper);
        String setSql = getSetSqlSegment(updateWrapper);
        mapper.update(null, updateWrapper);
        log.info("update {} : condition {}, set {}, op {}.", mapper.getClass().getSimpleName()
                , whereSqlSegment
                , setSql
                , UserHolder.getInstance().getUserId());
    }

    /**
     * 批量修改，自动填充审计lastModifiedDate lastModifiedBy
     * 跳过空值，null不会往数据库设置
     *
     * @param cmd
     * @param mapper
     * @param skipFields
     * @param <T>
     * @param <K>
     */
    public static <T, K> void updateBatchSkipEmpty(K cmd
            , BaseMapper<T> mapper
            , UpdateCallback<T, K> callback
            , String... skipFields) {
        UpdateWrapper<T> updateWrapper = updateWrapper(cmd, false, false, false, skipFields);
        updateWrapper = callback.callback(cmd, updateWrapper);
        String whereSqlSegment = getWhereSqlSegment(updateWrapper);
        String setSql = getSetSqlSegment(updateWrapper);
        mapper.update(null, updateWrapper);
        log.info("update {} : condition {}, set {}, op {}.", mapper.getClass().getSimpleName()
                , whereSqlSegment
                , setSql
                , UserHolder.getInstance().getUserId());
    }

    /**
     * 批量修改跳过审计，但是不跳过空值，值为null数据库为null
     * lastModifiedDate lastModifiedBy
     *
     * @param cmd
     * @param mapper
     * @param skipFields
     * @param <T>
     * @param <K>
     */
    public static <T, K> void updateBatchNotAudit(K cmd
            , BaseMapper<T> mapper
            , UpdateCallback<T, K> callback
            , String... skipFields) {
        UpdateWrapper<T> updateWrapper = updateWrapper(cmd, false, false, true, skipFields);
        updateWrapper = callback.callback(cmd, updateWrapper);
        String whereSqlSegment = getWhereSqlSegment(updateWrapper);
        String setSql = getSetSqlSegment(updateWrapper);
        mapper.update(null, updateWrapper);
        log.info("update {} : condition {}, set {}, op {}.", mapper.getClass().getSimpleName()
                , whereSqlSegment
                , setSql
                , UserHolder.getInstance().getUserId());
    }

    /**
     * 批量修改跳过审计
     * lastModifiedDate lastModifiedBy
     * 跳过空值null set
     *
     * @param cmd
     * @param mapper
     * @param skipFields
     * @param <T>
     * @param <K>
     */
    public static <T, K> void updateBatchNotAuditSkipEmpty(K cmd
            , BaseMapper<T> mapper
            , UpdateCallback<T, K> callback
            , String... skipFields) {
        UpdateWrapper<T> updateWrapper = updateWrapper(cmd, false, true, true, skipFields);
        updateWrapper = callback.callback(cmd, updateWrapper);
        String whereSqlSegment = getWhereSqlSegment(updateWrapper);
        String setSql = getSetSqlSegment(updateWrapper);
        mapper.update(null, updateWrapper);
        log.info("update {} : condition {}, set {}, op {}.", mapper.getClass().getSimpleName()
                , whereSqlSegment
                , setSql
                , UserHolder.getInstance().getUserId());
    }

    /**
     * 根据实体命令生成QueryWrapper条件
     *
     * @param qry
     * @param <T>
     * @return
     */
    public static <T> QueryWrapper<T> queryWrapper(Object qry, String... skipFields) {

        Map<String, Field> fieldMap = ReflectUtil.getFieldMap(qry.getClass());

        QueryWrapper<T> queryWrapper = new QueryWrapper<>();

        List<String> skipFieldList = Arrays.asList(skipFields);

        for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {

            // 如果字段要跳过则方法对这个字段不进行任何处理
            if (skipFieldList.stream().anyMatch(skipField -> ObjectUtil.equal(skipField, entry.getKey()))) {
                continue;
            }
            // 存在于跳过字段中则跳过，不处理条件
            if (SKIP_FIELDS.contains(entry.getKey())) {
                continue;
            }
            // 根据判断增加条件
            wrapper(qry, entry.getKey(), entry.getValue(), queryWrapper);
        }
        return queryWrapper;
    }

    /**
     * 根据实体命令生成QueryWrapper条件
     * @param qry
     * @param <T>
     * @return
     */
    public static <T> QueryWrapper<T> queryWrapper(Object qry){

        Map<String, Field> fieldMap = ReflectUtil.getFieldMap(qry.getClass());

        QueryWrapper<T> queryWrapper = new QueryWrapper<>();

        for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {

            // 存在于跳过字段中则跳过，不处理条件
            if(SKIP_FIELDS.contains(entry.getKey())){
                continue;
            }
            // 根据判断增加条件
            wrapper(qry, entry.getKey(), entry.getValue(), queryWrapper);
        }
        return queryWrapper;
    }

    /**
     * 根据指令生成UpdateWrapper
     * 如果skipSet=true, 则skipEmpty设置无效
     *
     * @param cmd        对象指令
     * @param skipSet    是否跳过set, 跳过set仅仅生成用于查询的wrapper
     * @param skipEmpty  是否跳过empty
     *                   , 如果跳过当值为Null或者size=0 以及""这种的时候不进行处理，反之设置为null
     * @param skipFields 要跳过的字段，跳过的字段方法就不会处理这种数据
     * @param <T>        实体类型
     * @return UpdateWrapper
     */
    public static <T> UpdateWrapper<T> updateWrapper(Object cmd
            , boolean skipSet
            , boolean skipEmpty
            , boolean skipAudit
            , String... skipFields) {

        Map<String, Field> fieldMap = ReflectUtil.getFieldMap(cmd.getClass());
        List<String> skipFieldList = Arrays.asList(skipFields);

        UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
        boolean isCondition = false;
        for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
            // 如果字段要跳过则方法对这个字段不进行任何处理
            if (skipFieldList.stream().anyMatch(skipField -> ObjectUtil.equal(skipField, entry.getKey()))) {
                continue;
            }
            // 在修改的过程中如果被标记条件则称为修改条件
            if (AnnotationUtil.hasAnnotation(entry.getValue(), Condition.class)) {
                wrapper(cmd, entry.getKey(), entry.getValue(), updateWrapper);
                isCondition = true;
            } else if (!skipSet) {
                // 如果没有被标记为条件则进行修改
                String fieldName = getDatabaseFieldName(-1, entry.getKey(), entry.getValue());
                Object fieldValue = ReflectUtil.getFieldValue(cmd, entry.getValue());
                if (ObjectUtil.isNotEmpty(fieldValue)) {
                    updateWrapper.set(fieldName, fieldValue);
                } else if (!skipEmpty) {
                    updateWrapper.set(fieldName, null);
                }
                // 是否跳过审计信息支持
                if (!skipAudit) {
                    // 审计信息支持
                    updateWrapper.set(camel2under("lastModifiedDate"), LocalDateTime.now());
                    updateWrapper.set(camel2under("lastModifiedBy"), UserHolder.getInstance().getUserId());
                }
            }
        }
        if (!isCondition) {
            throw new CheckException("updateWrapper方法调用必须存在条件@Condition!");
        }
        return updateWrapper;
    }

    /**
     * 获取where sql段用于打印
     * @param wrapper UpdateWrapper
     * @return sql
     * @param <T> T
     */
    private static <T> String getWhereSqlSegment(UpdateWrapper<T> wrapper){
        String sqlSegment = wrapper.getSqlSegment();

        for(Map.Entry<String,Object> entry : wrapper.getParamNameValuePairs().entrySet()){
            String repKey = "#{ew.paramNameValuePairs."+entry.getKey()+"}";
            sqlSegment = sqlSegment.replace(repKey, String.valueOf(entry.getValue()));
        }
        return sqlSegment;
    }

    /**
     * 获取where set sql段用于打印
     *
     * @param wrapper UpdateWrapper
     * @param <T>     T
     * @return sql
     */
    private static <T> String getSetSqlSegment(UpdateWrapper<T> wrapper) {
        String sqlSet = wrapper.getSqlSet();

        for (Map.Entry<String, Object> entry : wrapper.getParamNameValuePairs().entrySet()) {
            String repKey = "#{ew.paramNameValuePairs." + entry.getKey() + "}";
            sqlSet = sqlSet.replace(repKey, String.valueOf(entry.getValue()));
        }
        return sqlSet;
    }

    /**
     * 给wrapper增加条件
     * @param o 对象例如qry cmd
     * @param key 字段的名称，反射出来的
     * @param field 反射出来的字段Field对象
     * @param wrapper wrapper对象 可以是Query也可以是Update
     */
    private static void wrapper(Object o, String key, Field field, AbstractWrapper wrapper) {

        if (isConditional(key, EQ)) {
            int index = lastIndexOf(key, EQ);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.eq(fieldName, transformValue(value));
            }
        } else if (isConditional(key, LIKE)) {
            int index = lastIndexOf(key, LIKE);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, key);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.like(fieldName, transformValue(value));
            }
        } else if (isConditional(key, LIKE_LEFT)) {
            int index = lastIndexOf(key, LIKE_LEFT);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.likeLeft(fieldName, transformValue(value));
            }
        } else if (isConditional(key, LIKE_RIGHT)) {
            int index = lastIndexOf(key, LIKE_RIGHT);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.likeRight(fieldName, transformValue(value));
            }
        } else if (isConditional(key, GT)) {
            int index = lastIndexOf(key, GT);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.gt(fieldName, transformValue(value));
            }
        } else if (isConditional(key, LT)) {
            int index = lastIndexOf(key, LT);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.lt(fieldName, transformValue(value));
            }
        } else if (isConditional(key, GE)) {
            int index = lastIndexOf(key, GE);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.ge(fieldName, transformValue(value));
            }
        } else if (isConditional(key, LE)) {
            int index = lastIndexOf(key, LE);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.le(fieldName, transformValue(value));
            }
        } else if (isConditional(key, IS_NULL)) {
            int index = lastIndexOf(key, IS_NULL);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (value != null && !(value instanceof Boolean)) {
                throw new CheckException("is_null条件必须是Boolean类型");
            }
            if (value != null) {
                if ((Boolean) value) {
                    wrapper.isNull(fieldName);
                } else {
                    wrapper.isNotNull(fieldName);
                }
            }
        } else if (isConditional(key, NOT_IN)) {
            int index = lastIndexOf(key, NOT_IN);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.notIn(fieldName, (Collection<?>) value);
            }
        } else if (isConditional(key, IN)) {
            int index = lastIndexOf(key, IN);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.in(fieldName, (Collection<?>) value);
            }
        } else if (isConditional(key, SORT)) {
            int index = lastIndexOf(key, SORT);
            String fieldName = getDatabaseFieldName(index, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            // 必须传入值，不是DESC其他值均为ASC
            if (ObjectUtil.isNotEmpty(value)) {
                if (ObjectUtil.equal(String.valueOf(transformValue(value)).toUpperCase(), "DESC")) {
                    wrapper.orderByDesc(fieldName);
                } else {
                    wrapper.orderByAsc(fieldName);
                }
            }
        } else {
            // 如果字段后面什么条件都没有加就按照等于处理
            String fieldName = getDatabaseFieldName(-1, key, field);
            Object value = ReflectUtil.getFieldValue(o, field);
            if (ObjectUtil.isNotEmpty(value)) {
                wrapper.eq(fieldName, transformValue(value));
            }
        }
    }

    /**
     * 转换value
     *
     * @param value
     * @return
     */
    private static Object transformValue(Object value) {
        // TODO 暂无作用
        return value;
    }

    /**
     *  从后面查看字符串存不存在，严格判断大小写
     *  避免某些单词误判，例如enable
     * @param original 原字符串
     * @param condition 条件字符串，例如本类的静态变量LIKE
     * @return int index
     */
    private static int lastIndexOf(String original, String condition){
        return original.lastIndexOf(condition);
    }

    /**
     * 判断原字符串中有没有条件字符
     *
     * @param original  原字符
     * @param condition 条件字符
     * @return
     */
    private static boolean isConditional(String original, String condition) {
        return original.endsWith(condition);
    }

    /**
     * 根据前面查询的条件index以及原始字符串转换成数据库字段名
     * @param index lastIndexOfLower 返回index
     * @param original 原始字符串例如nameLike
     * @return 数据库字段名
     */
    private static String getDatabaseFieldName(int index, String original, Field field){
        // 如果字段存在别名，直接取别名中的值
        if(AnnotationUtil.hasAnnotation(field, Alias.class)) {
            Alias alias = field.getAnnotation(Alias.class);
            return alias.value();
        }
        if(index != -1){
            original = original.substring(0, index);
        }
        return camel2under(original);
    }

    /**
     * 功能：驼峰命名转下划线命名
     * 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
     */
    public static String camel2under(String c)
    {
        String separator = "_";
        c = c.replaceAll("([a-z])([A-Z])", "$1"+separator+"$2").toLowerCase();
        return c;
    }

    /**
     * 功能：下划线命名转驼峰命名
     * 将下划线替换为空格,将字符串根据空格分割成数组,再将每个单词首字母大写
     * @param s
     * @return
     */
    public static String under2camel(String s)
    {
        String separator = "_";
        String under="";
        s = s.toLowerCase().replace(separator, " ");
        String sarr[]=s.split(" ");
        for(int i=0;i<sarr.length;i++)
        {
            String w=sarr[i].substring(0,1).toUpperCase()+sarr[i].substring(1);
            under +=w;
        }
        return under;
    }

}
